﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FindArbitrageConsole.ArbitrageFindScenarios;
using FindArbitrageConsole.Data;
using FindArbitrageConsole.Data.Model;
using FindArbitrageConsole.Data.Repository;
using FindArbitrageConsole.Equals;
using Microsoft.Practices.Unity;

namespace FindArbitrageConsole
{
    public class ArbitrageModule
    {
        [Dependency]
        public IUnityContainer Container { get; set; }

        [Dependency]
        public IDataService DataService { get; set; }

        [Dependency]
        public KefRepository KefRepository { get; set; }

        [Dependency]
        public MaxKefRepository MaxKefRepository { get; set; }

        [Dependency]
        public ArbitrageLinkRepository ArbLinkRepository { get; set; }

        public void MainScenario()
        {
            var currentTime = DateTime.Now;

            var listEventIds = DataService.GetAllEvents();

            var loadedAllKefs = KefRepository.Kefs;
            var loadedAllMaxKefs = MaxKefRepository.MaxKefs;
            var loadedAllExtArbLinks = ArbLinkRepository.ArbitrageLinks;

            foreach (var eventId in listEventIds)
            {
                var allKefsByEvent = loadedAllKefs.Where(k=>k.EventId == eventId);
                var allMaxKefsByEvent = loadedAllMaxKefs.Where(k => k.EventId == eventId);
                //список вилок (список списков ArbLinkData) текущего event_id
                var allArbsByEvent = loadedAllExtArbLinks.Where(l => l.EventId == eventId).Select(l => new ArbitrageLinkData(l.ArbitrageId,l.KefId))
                                                             .GroupBy(k => k.ArbitrageId).Select(g => g.Select(l => l)).ToList();


                var arbDependencies = new List<IEnumerable<KefData>>();

                #region Выполняем все сценарии по получению вилок

                //Отдельный сценарий по получению вилок.
                arbDependencies.AddRange(Container.Resolve<ByOneMarketWithAllSelectionsScenario>().GetArbitrageDependency(allMaxKefsByEvent).ToList());

                //run all IArbitrageScenario scenarios
                //arbDependencies.Add(new IArbitrageScenario.GetArbitrageDependency();
                //arbDependencies.Add(new IArbitrageScenario.GetArbitrageDependency();
                //arbDependencies.Add(new IArbitrageScenario.GetArbitrageDependency();

                #endregion

                //из "вилок по макс кефа" и "всех кэфов по этому event_id" достаем вилки по всем кефам. (При этом уже кэфы преобразованы в ArbitrageLink)
                var arbDependenciesWithAllKefs = GetAllArbFromArbDependencies(arbDependencies, allKefsByEvent);

                if (!arbDependenciesWithAllKefs.Any())
                    continue;

                //Получаем список существующих вилок по eventId
                //var existingDependencies = GetExistingArbitrages(eventId);

                //Получаем список новых вилок (для записи в БД)
                var onlyNewArbitrages = GetUniqueArbs(arbDependenciesWithAllKefs, allArbsByEvent).ToList();

                //Получаем список вилок которые уже были в базе (для обновления их в БД)
                var updatedArbitrages = GetIntersectArbs(arbDependenciesWithAllKefs, allArbsByEvent).ToList();
                //Получаем список ID вилок которые необходимо обновить (п.с. может логика и не совсем корректна)
                var updatedArbIds = updatedArbitrages.Select(arb => arb.First().ArbitrageId);


                //Обновляем вновь найденные вилки которые уже есть в БД (обновляем им поле update_time)
                DataService.UpdateExistingArbitrages(updatedArbIds, currentTime);
                //Записываем в базу новые найденные вилки
                DataService.WriteNewArbitrages(onlyNewArbitrages, currentTime);
            }
            //End foreach. Обошли все ивенты и проставили(или обновили) в таблицах вилки

            //теперь удаляем из таблицы вилок те вилки, которые не нашли за этот проход.
            DataService.DeleteIrrelevantArbitrages(currentTime);

        }

        /// <summary>
        /// Возврщаем уникальные вилки из первого массива, которых нет во втором.
        /// </summary>
        /// <param name="listDependencies">список созданых(найденых) вилок</param>
        /// <param name="existingDependencies">список существующих в базе вилок</param>
        /// <returns>новые вилки (исключает те, которые уже есть в базе) отсортированные уже по KefID</returns>
        private IEnumerable<IEnumerable<ArbitrageLinkData>> GetUniqueArbs(IEnumerable<IEnumerable<ArbitrageLinkData>> listDependenciesSource, IEnumerable<IEnumerable<ArbitrageLinkData>> allDependencies)
        {
            //Сортируем исходы вилок (вилки) по KefID
            var sortedListDependencies = listDependenciesSource.Select(arb => arb.OrderBy(l => l.KefId).ToList());
            var sortedAllDependencies = allDependencies.Select(arb => arb.OrderBy(l => l.KefId).ToList());

            //Отсеиваем те, которые уже есть в базе
            var destinationListDependencies = sortedListDependencies.Where(arb => !ContainsInDependencies(sortedAllDependencies, arb));

            return destinationListDependencies;
        }

        /// <summary>
        /// Возвращает список вилок которые находятся и в первом и во втором массивах. (Пересечение)
        /// При этом делает сверку не первого со вторым а второго с первым. И возвращает по сути вилки из второго (т.к. у них проставлен arbitrage_id)
        /// </summary>
        /// <param name="listDependencies"></param>
        /// <param name="existingDependencies"></param>
        /// <returns></returns>
        private IEnumerable<IEnumerable<ArbitrageLinkData>> GetIntersectArbs(IEnumerable<IEnumerable<ArbitrageLinkData>> listDependenciesSource, IEnumerable<IEnumerable<ArbitrageLinkData>> allDependencies)
        {
            //Сортируем исходы вилок (вилки) по KefID
            var sortedListDependencies = listDependenciesSource.Select(arb => arb.OrderBy(l => l.KefId).ToList());
            var sortedAllDependencies = allDependencies.Select(arb => arb.OrderBy(l => l.KefId).ToList());

            //Оставляем те, которые есть в обеих массивах
            var destinationListDependencies = sortedAllDependencies.Where(arb => ContainsInDependencies(sortedListDependencies, arb));

            return destinationListDependencies;
        }

        /// <summary>
        /// Получаем список вилок по макс кефам. По этому списку получаем все необходимые кефы  с базы (не только максимальные) 
        /// и находим вилки среди всех этих кефов (по зависимостям которые уже пришли)
        /// </summary>
        /// <param name="arbDependencies"></param>
        /// <returns></returns>
        private IEnumerable<IEnumerable<ArbitrageLinkData>> GetAllArbFromArbDependencies(IEnumerable<IEnumerable<KefData>> arbDependencies, IEnumerable<KefData> allKefsByEvent)
        {
            //Будущий список вилок из всех кефов.
            var arbsWithAllKefs = new List<IEnumerable<ArbitrageLinkData>>();

            if (!arbDependencies.Any())
                return arbsWithAllKefs;

            foreach (var arb in arbDependencies)
            {
                //Список состоящий из списков кэфов по одному исходу. Итого если 3 исхода у нас будет list = трем массивам, отсортированным по selection_id. далее из этого списка получаем все вилки.
                var listGrouppedBySelections = arb.Select(kef => allKefsByEvent.Where(k => k.EventId == kef.EventId && k.MarketId == kef.MarketId && k.SelectionId == kef.SelectionId)).ToList();

                //лист вилок
                var listArbitragesFromKefs = CommonFuncs.GetPermutationsArbitrages(listGrouppedBySelections);

                //лист вилок только уже не из KefData а из ArbitrageLinkData
                var listArbitrageFromLinks = listArbitragesFromKefs.Select(arbtr => arbtr.Select(k => new ArbitrageLinkData(0, k.Id)));

                //Добавляем ко всем вилкам полученный массив вилок.
                arbsWithAllKefs.AddRange(listArbitrageFromLinks);
            }

            return arbsWithAllKefs;
        }

        /// <summary>
        /// sorted - Значит АрбЛинки в пределах одной вилки (group by ArbID) должны быть отсортированы по KefID.
        /// Принимает массив вилок (массив массивов) и сверяет содержит ли переданный массив вилок переданную вилку.
        /// </summary>
        /// <param name="sortedSource"></param>
        /// <param name="arbitrage"></param>
        /// <returns></returns>
        public bool ContainsInDependencies(IEnumerable<IEnumerable<ArbitrageLinkData>> sortedSource,IEnumerable<ArbitrageLinkData> sortedArbitrage)
        {
            return sortedSource.Any(arb => arb.SequenceEqual(sortedArbitrage, new ArbLinkComparer()));
        }
    }
}
